"""General utilities.
"""
from datetime import datetime
import itertools
from os import path
import unittest

import yaml

ROOTDIR = path.normpath(path.join(path.dirname(__file__), ".."))
DATADIR = path.join(ROOTDIR, "data")

def getAppConfig():
  CONFIG_PATH = path.join(ROOTDIR, "ui", "app.yaml")
  f = open(CONFIG_PATH, "r")
  return yaml.load(f)

def opendatafile(filename, *args, **kwargs):
  """Opens a file in the data directory. Same signature as the built-in method
  open().
  """
  p = path.join(DATADIR, filename)
  p = path.normpath(p)
  return open(p, *args, **kwargs)

def lookahead(iterable, n):
  """A generator function that yields slices of the specified length 'n', but
  still advances by only one with each yield.
  """
  items = []
  for item in iterable:
    items.append(item)
    if len(items) == n:
      yield items
      items = items[1:]

def chunks(iterable, size):
  """A generator function that yields chunks of the specified length 'size'.
  """
  it = iter(iterable)
  items = list(itertools.islice(it, size))
  while items:
    yield items
    items = list(itertools.islice(it, size))

def ignore_exception(exception=Exception, default_val=None):
  """Returns a decorator that ignores an exception raised by the function it
  decorates.
  
  Using it as a decorator:
    
    @ignore_exception(ValueError)
      def my_function():
        pass
  
  Using it as a function wrapper:
    
    int_try_parse = ignore_exception(ValueError)(int)
  """
  def decorator(function):
    def wrapper(*args, **kwargs):
      try:
        return function(*args, **kwargs)
      except exception:
        return default_val
    return wrapper
  return decorator

class Timer(object):
  """A utility that emulates basic stopwatch functionality.
  """
  def __init__(self):
    self.reset()
  
  def start(self):
    self._starttime = datetime.utcnow()
  
  def stop(self):
    self._endtime = datetime.utcnow()
    return self.check()
  
  def reset(self):
    self._starttime = None
    self._endtime = None
  
  def check(self):
    now = datetime.utcnow()
    if self._starttime is None:
      return now - now
    if self._endtime is None:
      return now - self._starttime
    return self._endtime - self._starttime

class BatchProcessor(object):
  def __init__(self, batch_size=10):
    batch_size = int(batch_size)
    assert batch_size > 0
    self.queue = []
    self.complete = []
    self.batch_size = batch_size
    self.timer = Timer()
    self.timer.start()
  
  def add(self, workitem):
    self.queue.append(workitem)
    if len(self.queue) == self.batch_size:
      self.processQueue()
  
  def finish(self):
    if len(self.queue) > 0:
      self.processQueue()
    self.timer.reset()
  
  def processQueue(self):
    self.execute(self.queue)
    self.complete.extend(self.queue)
    self.queue = []
    time = str(self.timer.check())
    print("Processed %i entities (%s elapsed)." % (len(self.complete), time))
  
  def execute(self, workitems):
    """Implement this method.
    """
    pass

class UtilTestCase(unittest.TestCase):
  def testOpenFile(self):
    opendatafile("Blocks.txt")
  
  def testBadFileName(self):
    self.assertRaises(IOError, opendatafile, "BadFileName.txt")
  
  def testGetAppConfig(self):
    config = getAppConfig()
    self.assertEqual(config["application"], "char-map")
